package com.cheetah.controller.protobuf;

import com.cheetah.annotation.ProtobufController;
import com.cheetah.annotation.RunExecutor;
import com.cheetah.config.ThreadPoolConfig;
import com.cheetah.dispatcher.RequestMapping;
import com.cheetah.thread.ThreadPoolManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import static com.cheetah.thread.ThreadPoolManager.HANDLER_POOL_PREFIX;

/**
 * @ClassName ProtobuffHandlerInitalizer
 * @Author Administrator
 * @Date 2019/7/19
 * @Version V1.0
 *
 * @Description: protobuf 协议handler管理类
 *  获取相关协议的hanlder类，并完成handler类与方法之前的映射关系
 **/
@Component
@Slf4j
public class ProtobufHandlerManager implements ApplicationContextAware {

    private static final String THREAD_POOL_DEFAULT = "default";
    private ApplicationContext applicationContext;

    private Map<String,Object> beans = new HashMap<>();

    private Map<String, RequestMapping> requestMappings = new HashMap<>();

    @Autowired
    private ThreadPoolManager threadPoolManager;

    @Autowired
    private ThreadPoolConfig config;

    @PostConstruct
    public void init(){
        beans = applicationContext.getBeansWithAnnotation(ProtobufController.class);

        beans.forEach((k,v) ->{
            Method[] methods = v.getClass().getDeclaredMethods();

            if(methods != null){
                Arrays.stream(methods).forEach(m -> {
                    RequestMapping requestMapping = new RequestMapping();
                    requestMapping.setHandler(v);
                    requestMapping.setMethod(m);
                    com.cheetah.annotation.RequestMapping mapping = m.getAnnotation(com.cheetah.annotation.RequestMapping.class);

                    //如果方法中设置了线程池，则优先使用，如果没有设置则检查类是否设置，如果有则使用，如果都没有使用默认线程池
                    if(m.isAnnotationPresent(RunExecutor.class)){
                        String poolName = m.getAnnotation(RunExecutor.class).value();

                        //没有设置线程池组，使用默认线程池
                        if(StringUtils.isBlank(poolName) || config.getPoolMap().get(poolName) == null){
                            poolName = THREAD_POOL_DEFAULT;
                        }
                        requestMapping.setExecutor(threadPoolManager.getExecutorFactory().getExecutor(HANDLER_POOL_PREFIX + poolName));
                    }else if(v.getClass().isAnnotationPresent(RunExecutor.class) ){

                        String poolName = v.getClass().getAnnotation(RunExecutor.class).value();

                        //没有设置线程池组，使用默认线程池
                        if(StringUtils.isBlank(poolName)|| config.getPoolMap().get(poolName) == null){
                            poolName = THREAD_POOL_DEFAULT;
                        }
                        requestMapping.setExecutor(threadPoolManager.getExecutorFactory().getExecutor(HANDLER_POOL_PREFIX + poolName));
                    }else {
                        requestMapping.setExecutor(threadPoolManager.getExecutorFactory().getExecutor(HANDLER_POOL_PREFIX + THREAD_POOL_DEFAULT));
                    }

                    requestMapping.setAuth(mapping.auth());
                    requestMapping.setMapping(mapping.mapping());
                    requestMappings.put(mapping.mapping(),requestMapping);
                    log.info("init handler mapping: {} => {}   executor[pool-name:{},count:{},queue:{},timout:{}] ",
                            v.getClass().getName(),
                            m.getName(),
                            requestMapping.getExecutor().getPoolName(),
                            requestMapping.getExecutor().getPoolSize(),
                            requestMapping.getExecutor().getQueueSize(),
                            requestMapping.getExecutor().getTimeout());
                });
            }

        });
    }

    public Map<String,Object> handlerMapping(){
        return beans;
    }

    public Map<String, RequestMapping> requestMappings(){
        return requestMappings;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
